package org.artifact.core.db;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.artifact.core.lang.IServer;
import org.bson.Document;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.DeleteOneModel;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.ReplaceOneModel;
import com.mongodb.client.model.ReplaceOptions;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

public abstract class MongoDBDao<T extends BaseBean<T>> extends BaseDao<T>{

	static final Log log = LogFactory.get(MongoDBDao.class);

	protected MongoDatabase getMongoDatabase(){
		return IServer.me().getContext().getMongoDatabase();
	}
	
	@Override
	protected void finishSaveCache() {
		List<ReplaceOneModel<Document>> requests = new ArrayList<ReplaceOneModel<Document>>();
		for (Entry<Object, T> e : cachedUpdates.entrySet()) {
			T bean = e.getValue();
			Map map = (Map) bean.write();
			requests.add(new ReplaceOneModel<Document>(new Document().append(getPKFieldName(), e.getKey()), new Document(map),new ReplaceOptions().upsert(true)));
		}
		getCollection().bulkWrite(requests);
	}

	@Override
	protected void finishDeleteCache() {
		List<DeleteOneModel<Document>> requests = new ArrayList<DeleteOneModel<Document>>();
		for (Entry<Object, T> e : cachedDeletes.entrySet()) {
			requests.add(new DeleteOneModel<>(Filters.eq(getPKFieldName(), e.getKey())));
		}
		getCollection().bulkWrite(requests);
	}
	
	
	
	@Override
	public boolean existsTable() {
		return getMongoDatabase().listCollectionNames().into(new ArrayList<String>()).contains(getTableName());
	}

	@Override
	public List<T> findAll() {
		return resultList(()->{
			List<T> list = new ArrayList<>();
			MongoCollection<Document> collection = getCollection();
			MongoCursor<Document> mongoCursor =collection.find().iterator();
			while (mongoCursor.hasNext()) {
				list.add(createEntity().read(mongoCursor.next()));
			}
			return list;
		}, t -> true);
	}

	@Override
	public T findByPrimaryKey(Object primaryKey) {
		return resultFirst(()->{
			MongoCollection<Document> collection = getCollection();
			Document document = collection.find(Filters.eq(getPKFieldName(), primaryKey)).first();
			if (document!=null) {
				return createEntity().read(document);
			}
			return null;
		}, t -> ObjectUtil.equal(t.pk(), primaryKey)); 
	}

	protected MongoCollection<Document> getCollection() {
		return getMongoDatabase().getCollection(getTableName());
	}
}
